import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 服务器获取客户端链接
 * 检查命令
 * 执行命令
 */

public class MyServer {
    private static final int PORT = 8917; // 监听命令端口
    private static final int FILE_PORT = 8918; // 监听文件传输端口
    static ExecutorService commandExecutorService = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池处理命令
    static ExecutorService fileTransferExecutorService = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池处理文件传输

    public static void main(String[] args) {
        // 启动命令监听线程
        new Thread(() -> {
            try (ServerSocket serverSocket = new ServerSocket(PORT)) { // 监听命令端口
                while (true) {
                    try {
                        PrintUnitl.println("等待客户端命令连接...");
                        Socket clientSocket = serverSocket.accept(); // 等待客户端连接
                        PrintUnitl.println("与客户端命令连接成功");
                        commandExecutorService.submit(new ClientHandler(clientSocket)); // 为每个客户端创建一个线程
                        PrintUnitl.println("命令线程池+1线程");
                    } catch (Exception e) {
                        try (FileWriter fileWriter = new FileWriter("error.txt", true);
                             PrintWriter printWriter = new PrintWriter(fileWriter)) {
                            e.printStackTrace(printWriter);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 启动文件传输监听线程
        new Thread(() -> {
            try (ServerSocket fileServerSocket = new ServerSocket(FILE_PORT)) { // 监听文件传输端口
                while (true) {
                    try {
                        PrintUnitl.println("等待客户端文件传输连接...");
                        Socket clientSocket = fileServerSocket.accept(); // 等待客户端连接
                        PrintUnitl.println("与客户端文件传输连接成功");
                        fileTransferExecutorService.submit(new ClientHandler(clientSocket)); // 为每个客户端创建一个线程
                        PrintUnitl.println("文件传输线程池+1线程");
                    } catch (Exception e) {
                        try (FileWriter fileWriter = new FileWriter("error.txt", true);
                             PrintWriter printWriter = new PrintWriter(fileWriter)) {
                            e.printStackTrace(printWriter);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        //启用一个专门的线程来显示所有的线程池的情况
//        new Thread(() -> {
//            while (true) {
//                ThreadPoolExecutor fileThreadPoolExecutor = (ThreadPoolExecutor) fileTransferExecutorService;
//                ThreadPoolExecutor commandThreadPoolExecutor = (ThreadPoolExecutor) commandExecutorService;
//                // 移动光标到固定位置
////                System.out.print("\033[2J"); // 清屏
////                System.out.printf("| %-20s | %-20s | %-20s | %-20s |\n",
////                        "线程池类型", "线程池大小", "正在执行任务数", "核心线程数");
////                System.out.printf("| %-20s | %-20d | %-20d | %-20d |\n",
////                        "文件传输线程池", fileThreadPoolExecutor.getPoolSize(),
////                        fileThreadPoolExecutor.getActiveCount(), fileThreadPoolExecutor.getCorePoolSize());
////                System.out.printf("| %-20s | %-20d | %-20d | %-20d |\n",
////                        "命令线程池", commandThreadPoolExecutor.getPoolSize(),
////                        commandThreadPoolExecutor.getActiveCount(), commandThreadPoolExecutor.getCorePoolSize());
//
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//
//
//            }
//        }).start();
    }
}

class ClientHandler implements Runnable {
    private Socket clientSocket;// 客户端套接字

    private JSONObject clientJson;// 客户端发送的json
    private SendFolder sendFolder = new SendFolder();

    public ClientHandler(Socket clientSocket) {
        this.clientSocket = clientSocket;
    }

    /**
     * 当使用实现接口｛@code Runnable｝的对象时
     * 要创建线程，启动线程会导致对象
     * 在单独执行的方法中调用{@code run}方法
     * 线.
     * 方法｛@code run｝的一般约定是，它可以
     * 采取任何行动。
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        PrintUnitl.println("与客户端连接成功");
        try {
            DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());// 创建数据输入工厂
            PrintUnitl.println("等待客户端命令数据中……");
            String clientMessage = dataInputStream.readUTF();// 读取客户端发送的消息
            clientJson = new JSONObject(clientMessage);// 将客户端发送的消息转换为json
            PrintUnitl.println("客户端发送的命令：" + clientJson.getString("Command"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        /*
        {
        "Command": "SendFolder",
        "SendFolder": "",
        "SendFileNames": "",
        "SendFileLengths": "",
        "GetFolder": "",
        "GetFileNames": "",
        "GetFileLengths": "",
        }

        Json命令解析
        String Command = clientJson.getString("Command");// 获取客户端发送的命令

        String SendFolder = clientJson.getString("SendFolder");// 获取客户端请求的文件夹地址
        String SendFileNames = clientJson.getString("SendFileNames");// 获取客户端请求的文件名列表
        String SendFileLengths = clientJson.getString("SendFileLengths");// 获取客户端请求的文件长度列表

        String GetFolder = clientJson.getString("GetFolder");// 获取客户端上传的文件夹地址
        String GetFileNames = clientJson.getString("GetFileNames");// 获取客户端上传的文件名列表
        String GetFileLengths = clientJson.getString("GetFileLengths");// 获取客户端上传的文件长度列表
         */
        switch (clientJson.getString("Command")) {
            case "SendFolder":// 服务器发送文件夹内容
                PrintUnitl.println("服务器发送文件夹内容");
                String clientPath = clientJson.getString("SendFolder");// 获取客户端请求的文件夹地址
                PrintUnitl.println("客户端请求的文件夹路径:"+clientPath);
                if (clientPath.equals("")) {
                    PrintUnitl.println("准备文件夹数据");
                    JSONObject Folder = sendFolder.getFolderData();
                    try {
                        PrintUnitl.println("文件夹数据准备完成");
                        PrintUnitl.println("准备数据输出");
                        DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());// 创建数据输出工厂
                        dataOutputStream.writeUTF(Folder.toString());// 发送文件夹内容
                        PrintUnitl.println("数据输出完成");
                        clientSocket.close();
                        return;
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    JSONObject Folder = sendFolder.getFolderData(clientPath);
                    try {
                        DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());// 创建数据输出工厂
                        dataOutputStream.writeUTF(Folder.toString());
                        dataOutputStream.flush();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }

                }
                break;
            case "SendFile":// 服务器发送文件
                PrintUnitl.println("服务器发送文件");
                try {
                    PrintUnitl.println("准备文件输出");
                    PrintUnitl.println("准备文件数据");
                    PrintUnitl.println(String.valueOf(clientJson));
                    JSONArray SendFileNames= clientJson.getJSONArray("SendFileNames");// 获取客户端请求的文件名
                    JSONArray SendFileLengths= clientJson.getJSONArray("SendFileLengths");// 获取客户端请求的文件长度

                    PrintUnitl.println("文件数据准备完成");
                    PrintUnitl.println("准备发送文件");
                    SendFile sendFile = new SendFile(SendFileNames,SendFileLengths,clientSocket);
                    sendFile.sendFile();
                    PrintUnitl.println("文件发送完成");
                    clientSocket.close();
                    return;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            case "GetFolder":// 服务器下载客户端文件夹内容
                break;
            case "GetFile":// 服务器下载客户端文件
                break;
            case "OUT":// 服务器断开连接
                break;
        }
    }

}
class SendFile {
    private JSONArray sendFileNames;
    private JSONArray sendFileLengths;
    private DataOutputStream dataOutputStream;
    private DataInputStream dataInputStream;
    private Socket clientSocket;
    SendFile(JSONArray sendFileNames, JSONArray sendFileLengths, Socket clientSocket) throws IOException {
        PrintUnitl.println("初始化");
        this.sendFileNames = sendFileNames;
        this.sendFileLengths = sendFileLengths;
        this.clientSocket = clientSocket;
        try {
            this.dataOutputStream = new DataOutputStream(this.clientSocket.getOutputStream());// 创建数据输出工厂
        }catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void sendFile() {
        //PrintUnitl.print("开始发送文件,待下载队列为：");
        for (int i = 0; i < sendFileNames.length(); i++){
            //PrintUnitl.print("文件名:"+sendFileNames.get(i)+" 文件长度:"+sendFileLengths.get(i));
        }
        for (int i = 0; i < sendFileNames.length(); i++){
            PrintUnitl.println("准备发送文件名:"+sendFileNames.get(i)+" 文件长度:"+sendFileLengths.get(i));
            String sendFileName = (String) sendFileNames.get(i);
            long sendFileLength = Long.parseLong(String.valueOf(sendFileLengths.get(i)));
            File file = new File(sendFileName);// 创建文件对象，用于获取文件长度
            long fileSize = file.length();
            long seekSize=fileSize-sendFileLength;// 计算已下载的长度
            PrintUnitl.println("文件总长度:"+fileSize+" 已下载长度:"+seekSize);
            ConsoleProgressBar progressBar = new ConsoleProgressBar(fileSize, 50, '#', '-');
            try (RandomAccessFile raf = new RandomAccessFile(sendFileName, "r")) {
                raf.seek(seekSize); // 跳过已下载的部分
                PrintUnitl.println("已下载长度:"+seekSize);
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = raf.read(buffer)) != -1) {
                    //PrintUnitl.println("写入输出流的文件data:"+ Arrays.toString(buffer));
                    dataOutputStream.write(buffer, 0, bytesRead);
                    dataOutputStream.flush();
                    progressBar.updateProgress(bytesRead);
                }
                //dataOutputStream.writeBytes("FILE_END");
                dataOutputStream.flush();
                PrintUnitl.println('\n'+sendFileName+" 文件数据输出完成");
                //获取用户接收完成的标志位
                dataInputStream= new DataInputStream(clientSocket.getInputStream());// 创建数据输入工厂
                String clientFileOKFlag=dataInputStream.readUTF();
                if (clientFileOKFlag.equals("FILE_OK")){
                    PrintUnitl.println("文件接收完成");
                }else {
                    PrintUnitl.println("文件接收失败");
                }
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }

}
/**
 * 获取文件夹内容
 * 文件夹内容处理工厂
 */
class SendFolder {
    /*
        {
          "folders": ["folder1", "folder2"],
          "files": {
            "file1.txt": 1024,
            "file2.txt": 2048
          }
        }
        */
    private String path = "F:/";//文件夹地址
    private final JSONObject FolderData=new JSONObject();//文件夹内容

    SendFolder() {
        File FolderName = new File(path);
        File[] FolderNameData = FolderName.listFiles();//获取文件夹内的文件和文件夹列表
        List<String> FolderNameList = new ArrayList<>();//文件夹名列表
        Map<String, Long> FileNameList = new HashMap<>();//文件名和大小列表
        for (File file : FolderNameData) {
            if (file.isDirectory()) {
                FolderNameList.add(file.getAbsolutePath());
            } else {
                FileNameList.put(file.getAbsolutePath(), file.length());
            }
        }
        FolderData.put("folders", FolderNameList);
        FolderData.put("files", FileNameList);
    }

    public JSONObject getFolderData() {
        return FolderData;
    }

    public JSONObject getFolderData(String path) {
        this.path = path;
        File FolderName = new File(path);
        File[] FolderNameData = FolderName.listFiles();//获取文件夹内的文件和文件夹列表
        List<String> FolderNameList = new ArrayList<>();//文件夹名列表
        Map<String, Long> FileNameList = new HashMap<>();//文件名和大小列表
        if (FolderNameData != null) {
            for (File file : FolderNameData) {
                if (file.isDirectory()) {
                    FolderNameList.add(file.getAbsolutePath());
                } else {
                    FileNameList.put(file.getAbsolutePath(), file.length());
                }
            }
        }
        FolderData.put("folders", FolderNameList);
        FolderData.put("files", FileNameList);
        return FolderData;
    }

    public void setPath(String path) {
        this.path = path;
        File FolderName = new File(path);
        File[] FolderNameData = FolderName.listFiles();//获取文件夹内的文件和文件夹列表
        List<String> FolderNameList = new ArrayList<>();//文件夹名列表
        Map<String, Long> FileNameList = new HashMap<>();//文件名和大小列表
        for (File file : FolderNameData) {
            if (file.isDirectory()) {
                FolderNameList.add(file.getAbsolutePath());
            } else {
                FileNameList.put(file.getAbsolutePath(), file.length());
            }
        }
        FolderData.put("folders", FolderNameList);
        FolderData.put("files", FileNameList);
    }
}

